#include "Wt.h"

/* uploadSettings command */

void WtUploadSettingsUsage(Tcl_Interp *interp, Tcl_Obj *cmd)
{
  Tcl_Obj *res = Tcl_GetObjResult(interp);
  char *cmdStr = WtToString(cmd);
  Tcl_AppendStringsToObj(res, "Incorrect usage. Should be:\n",
    cmdStr, " postMax ?size?\n",
    cmdStr, " allowUploads ?flag?\n",
    cmdStr, " tempDir ?path?\n",
    cmdStr, " uploadHandler ?script?\n",
    cmdStr, " uploadHandlerData ?data?\n",
    cmdStr, " table\n",
    NULL);
}

int WtUploadSettingsCmd(ClientData clientData, Tcl_Interp *interp, int objc,
                        Tcl_Obj *const objv[])
{
  int ret = TCL_ERROR, intVal;
  Tcl_Obj *interpRes = Tcl_GetObjResult(interp), *tbl;
  const char *subCmd;
  char *str;
  WtContext *w;

  w = (WtContext *)Tcl_GetAssocData(interp, "wt::context", NULL);

  if (objc < 2) {
    WtUploadSettingsUsage(interp, objv[0]);
    return TCL_ERROR;
  }

  subCmd = WtToString(objv[1]);

  if (!strcmp(subCmd, "postMax")) {

    /* postMax command */

    if (objc != 2 && objc != 3) {
      Tcl_AppendStringsToObj(interpRes, "Incorrect usage. Should be: ",
        WtToString(objv[0]), " ", WtToString(objv[1]), " ?size?");
    } else if (objc == 2) {
      Tcl_SetObjResult(interp, Tcl_NewIntObj(w->active->request.apWeb->post_max));
      ret = TCL_OK;
    } else {
      if (Tcl_GetIntFromObj(interp, objv[2], &intVal) == TCL_OK) {
        ApacheRequest_set_post_max(w->active->request.apWeb, intVal);
        Tcl_SetObjResult(interp, Tcl_NewIntObj(w->active->request.apWeb->post_max));
        ret = TCL_OK;        
      }
    }

  } else if (!strcmp(subCmd, "allowUploads")) {

    /* allowUploads command */

    if (objc != 2 && objc != 3) {
      Tcl_AppendStringsToObj(interpRes, "Incorrect usage. Should be: ",
        WtToString(objv[0]), " ", WtToString(objv[1]), " ?flag?");
    } else if (objc == 2) {
      Tcl_SetObjResult(interp, Tcl_NewBooleanObj(!w->active->request.apWeb->disable_uploads));
      ret = TCL_OK;
    } else {
      if (Tcl_GetBooleanFromObj(interp, objv[2], &intVal) == TCL_OK) {
        w->active->request.apWeb->disable_uploads = !intVal;
        Tcl_SetObjResult(interp,
          Tcl_NewBooleanObj(!w->active->request.apWeb->disable_uploads));
        ret = TCL_OK;
      }
    }

  } else if (!strcmp(subCmd, "tempDir")) {

    /* tempDir command */

    if (objc != 2) {
      Tcl_AppendStringsToObj(interpRes, "Incorrect usage. Should be: ",
        WtToString(objv[0]), " ", WtToString(objv[1]), " ?path?");
    } else if (objc == 2) {
      Tcl_SetObjResult(interp, WtNewString(w->active->request.apWeb->temp_dir));
      ret = TCL_OK;
    } else {
      str = WtToString(objv[2]);
      ApacheRequest_set_temp_dir(w->active->request.apWeb,
        ap_pstrdup(w->active->request.apReq->pool, str));
      Tcl_SetObjResult(interp, WtNewString(w->active->request.apWeb->temp_dir));
      ret = TCL_OK;
    }

  } else if (!strcmp(subCmd, "uploadHandler")) {

    /* uploadHandler command */

    if (objc != 2) {
      Tcl_AppendStringsToObj(interpRes, "Incorrect usage. Should be: ",
        WtToString(objv[0]), " ", WtToString(objv[1]), " ?command?");
    } else if (objc == 2) {
      Tcl_SetObjResult(interp, w->active->request.uploadHandler);
      ret = TCL_OK;
    } else {
      w->active->request.uploadHandler = objv[2];
      Tcl_IncrRefCount(w->active->request.uploadHandler);
      Tcl_SetObjResult(interp, w->active->request.uploadHandler);
      ret = TCL_OK;
    }

  } else if (!strcmp(subCmd, "uploadHandlerData")) {

    /* uploadHandlerData command */

    if (objc != 2) {
      Tcl_AppendStringsToObj(interpRes, "Incorrect usage. Should be: ",
        WtToString(objv[0]), " ", WtToString(objv[1]), " ?command?");
    } else if (objc == 2) {
      Tcl_SetObjResult(interp,
                       w->active->request.uploadHandlerData ?
                       w->active->request.uploadHandlerData : WtNewString(""));
      ret = TCL_OK;
    } else {
      if (w->active->request.uploadHandlerData) {
        Tcl_DecrRefCount(w->active->request.uploadHandlerData);
      }
      w->active->request.uploadHandlerData = objv[2];
      Tcl_IncrRefCount(w->active->request.uploadHandlerData);
      Tcl_SetObjResult(interp, w->active->request.uploadHandlerData);
      ret = TCL_OK;
    }

  } else if (!strcmp(subCmd, "table")) {

    /* table command - Create a table with the settings for debugging. */

    tbl = WtNewTableObj(NULL);

    WtTableSetStrToInt(tbl, "postMax", w->active->request.apWeb->post_max);
    WtTableSetStrToBool(tbl, "allowUploads", !w->active->request.apWeb->disable_uploads);
    WtTableSetStrToStr(tbl, "tempDir", WtSafeStr(w->active->request.apWeb->temp_dir));
    WtTableSetStrToObj(tbl, "uploadHandler", w->active->request.uploadHandler);
    WtTableSetStrToObj(tbl, "uploadHandlerData", w->active->request.uploadHandlerData ?
                       w->active->request.uploadHandlerData : WtNewString(""));

    Tcl_SetObjResult(interp, tbl);

    ret = TCL_OK;

  } else {

    /* unknown command */

    WtUploadSettingsUsage(interp, objv[0]);

  }

  return ret;
}

/* uploads command */

int WtUploadsCmd(ClientData clientData, Tcl_Interp *interp,
    int objc, Tcl_Obj *const objv[])
{
  int ret;
  WtContext *w = Tcl_GetAssocData(interp, "wt::context", NULL);

  if (!WtCmdInitRequest(interp)) {
    return TCL_ERROR;
  }

  WtTblMethodHelper(clientData, interp, objc, objv, &ret,
    w->active->request.uploads, 0, 1);

  return ret;
}

/* upload object type */

static void WtUploadFree(Tcl_Obj *obj)
{
  WtDeletePtr(obj);
  ckfree((char *)WtToUpload(obj));
}

static void WtUploadDup(Tcl_Obj *src, Tcl_Obj *dst)
{
  WtUpload *srcRep = WtToUpload(src);
  WtUpload *dstRep = (WtUpload *)ckalloc(sizeof(WtUpload));
  memcpy(dstRep, srcRep, sizeof(WtUpload));
  dst->internalRep.twoPtrValue.ptr1 = dstRep;
  dst->typePtr = &WtUploadType;
}

static void WtUploadUpdateString(Tcl_Obj *obj)
{
  WtUpdatePtrString(obj);
}

static int WtUploadSetFromAny(Tcl_Interp *interp, Tcl_Obj *obj)
{
  return WtSetPtrFromAny(interp, obj, &WtUploadType);
}

struct Tcl_ObjType WtUploadType =
{
  "upload",
  WtUploadFree,
  WtUploadDup,
  WtUploadUpdateString,
  WtUploadSetFromAny
};

/* Create a new upload object */

Tcl_Obj *WtNewUploadObj(ApacheUpload *item)
{
  Tcl_Obj *obj = Tcl_NewObj();
  WtUpload *upload = (WtUpload *)ckalloc(sizeof(WtUpload));
  upload->item = item;
  upload->channelCreated = 0;
  obj->internalRep.twoPtrValue.ptr1 = upload;
  obj->typePtr = &WtUploadType;
  Tcl_InvalidateStringRep(obj);
  return obj;
}

/* Get the internal representation of an upload object */

WtUpload *WtToUpload(Tcl_Obj *obj)
{
  return (WtUpload *)(obj->internalRep.twoPtrValue.ptr1);
}

/* upload command */

/* Get the upload object referenced by a parameter */

Tcl_Obj *WtUploadCmdGetUpload(Tcl_Interp *interp, Tcl_Obj *obj)
{
  Tcl_Obj *found = WtGetPtr(obj, &WtUploadType);

  if (!found) {
    Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
      "Parameter is not a valid upload object.");
  }

  return found;
}

void WtUploadUsage(Tcl_Interp *interp, Tcl_Obj *cmd)
{
  Tcl_Obj *res = Tcl_GetObjResult(interp);
  char *cmdStr = WtToString(cmd);
  Tcl_AppendStringsToObj(res, "Incorrect usage. Should be:\n",
    cmdStr, " path upload\n",
    cmdStr, " contentType upload\n",
    cmdStr, " size upload\n",
    cmdStr, " channel upload\n",
    cmdStr, " table upload\n");
}

int WtUploadCmd(ClientData clientData, Tcl_Interp *interp, int objc,
    Tcl_Obj *const objv[])
{
  int ret = TCL_ERROR;
  Tcl_Obj *interpRes = Tcl_GetObjResult(interp), *tbl;
  const char *subCmd;
  char *val;
  WtContext *w;
  Tcl_Obj *uploadObj;
  WtUpload *upload;
  FILE *uploadFile;
  Tcl_Channel uploadChan;

  w = (WtContext *)Tcl_GetAssocData(interp, "wt::context", NULL);

  if (objc < 2) {
    WtUploadUsage(interp, objv[0]);
    return TCL_ERROR;
  }

  subCmd = WtToString(objv[1]);

  /* Read the POST data if needed */

  if (!WtCmdInitRequest(interp)) {
    return TCL_ERROR;
  }

  if (!strcmp(subCmd, "path")) {

    /* path command */

    if (objc != 3) {
      Tcl_AppendStringsToObj(interpRes, "Incorrect usage. Should be: ",
        WtToString(objv[0]), " ", WtToString(objv[1]), " upload");
    } else {
      uploadObj = WtUploadCmdGetUpload(interp, objv[2]);
      if (uploadObj) {
        upload = WtToUpload(uploadObj);
        Tcl_SetObjResult(interp, WtNewString(
          upload->item->filename ? upload->item->filename : ""));
        ret = TCL_OK;
      }
    }

  } else if (!strcmp(subCmd, "contentType")) {

    /* contentType command */

    if (objc != 3) {
      Tcl_AppendStringsToObj(interpRes, "Incorrect usage. Should be: ",
        WtToString(objv[0]), " ", WtToString(objv[1]), " upload");
    } else {
      uploadObj = WtUploadCmdGetUpload(interp, objv[2]);
      if (uploadObj) {
        upload = WtToUpload(uploadObj);
        Tcl_SetObjResult(interp, WtNewString(
          ApacheUpload_type(upload->item)));
        ret = TCL_OK;
      }
    }

  } else if (!strcmp(subCmd, "size")) {

    /* size command */

    if (objc != 3) {
      Tcl_AppendStringsToObj(interpRes, "Incorrect usage. Should be: ",
        WtToString(objv[0]), " ", WtToString(objv[1]), " upload");
    } else {
      uploadObj = WtUploadCmdGetUpload(interp, objv[2]);
      if (uploadObj) {
        upload = WtToUpload(uploadObj);
        Tcl_SetObjResult(interp, Tcl_NewIntObj(
          ApacheUpload_size(upload->item)));
        ret = TCL_OK;
      }
    }

  } else if (!strcmp(subCmd, "channel")) {

    /* channel command */

    if (objc != 3) {
      Tcl_AppendStringsToObj(interpRes, "Incorrect usage. Should be: ",
        WtToString(objv[0]), " ", WtToString(objv[1]), " upload");
    } else {
      if (uploadObj = WtUploadCmdGetUpload(interp, objv[2])) {
        upload = WtToUpload(uploadObj);
        if (upload->channelCreated) {
          Tcl_SetObjResult(interp, WtNewString(
            Tcl_GetChannelName(upload->channel)));
        } else if (uploadFile = ApacheUpload_FILE(upload->item)) {
#ifdef __MINGW32__
          uploadChan = Tcl_MakeFileChannel((ClientData)_get_osfhandle(
            _fileno(uploadFile)), TCL_READABLE);
#else
          uploadChan = Tcl_MakeFileChannel((ClientData)_fileno(
            uploadFile), TCL_READABLE);
#endif
          Tcl_RegisterChannel(interp, uploadChan);
          Tcl_SetObjResult(interp, WtNewString(
            Tcl_GetChannelName(uploadChan)));
          ret = TCL_OK;
        }
      }

    }

  } else if (!strcmp(subCmd, "table")) {

    /* table command - Create a table with the item's properties
       for debugging. */

    if (objc != 3) {
      Tcl_AppendStringsToObj(interpRes, "Incorrect usage. Should be: ",
        WtToString(objv[0]), " ", WtToString(objv[1]), " upload");
    } else {
      uploadObj = WtUploadCmdGetUpload(interp, objv[2]);
      if (uploadObj) {
        upload = WtToUpload(uploadObj);
        tbl = WtNewTableObj(NULL);

        val = WtSafeStr(upload->item->filename);
        WtTableSetStrToStr(tbl, "path", val);

        val = WtSafeStr(ApacheUpload_type(upload->item));
        WtTableSetStrToStr(tbl, "contentType", val);

        WtTableSetStrToInt(tbl, "size",
          ApacheUpload_size(upload->item));

        if (upload->channelCreated) {
          val = WtSafeStr(Tcl_GetChannelName(upload->channel));
        } else {
          val = "";
        }

        WtTableSetStrToStr(tbl, "channel", val);

        Tcl_SetObjResult(interp, tbl);

        ret = TCL_OK;
      }
    }

  } else {

    /* Unknown command */

    WtUploadUsage(interp, objv[0]);

  }

  return ret;
}

void WtInitUploadCommands(Tcl_Interp *interp)
{
  WtContext *w = (WtContext *)Tcl_GetAssocData(interp, "wt::context", NULL);

  WtApLog(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, w,
    "Wtcl: WtInitUploadCommands.");

  Tcl_RegisterObjType(&WtUploadType);

  Tcl_CreateObjCommand(interp, "::wt::uploadSettings",
                       WtUploadSettingsCmd, NULL, NULL);

  Tcl_CreateObjCommand(interp, "::wt::uploads",
                       WtUploadsCmd, NULL, NULL);

  Tcl_CreateObjCommand(interp, "::wt::upload",
                       WtUploadCmd, NULL, NULL);
}
